package main

import (
	"crypto/md5"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"strings"
	"time"

	"github.com/Desserts/notifier"
	"github.com/Desserts/rmcomment"
	log "github.com/Sirupsen/logrus"
	"github.com/Unknwon/goconfig"
	"github.com/go-xorm/xorm"
)

var config *goconfig.ConfigFile
var path string
var sync bool
var conf string
var engine *xorm.Engine
var email notifier.EmailNotifier
var to string // emails who receive the notification, multiple emails slipt with ';'

type Record struct {
	Name   string
	Md5    string
	Expire bool
	Sync   bool
}

func init() {
	flag.BoolVar(&sync, "sync", false, "if sync the db")
	flag.StringVar(&conf, "conf", "config.ini", "config file")
	flag.Parse()
	var err error
	config, err = goconfig.LoadConfigFile(conf)
	if err != nil {
		log.Error("read config file error", err)
	}

	user, _ := config.GetValue("database", "user")
	pwd, _ := config.GetValue("database", "pwd")
	host, _ := config.GetValue("database", "host")
	port, _ := config.GetValue("database", "port")
	db, _ := config.GetValue("database", "db")

	engine, err = xorm.NewEngine("mysql", fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8", user, pwd, host, port, db))
	if err != nil {
		log.Error("create engine error ", err)
	}

	to, _ = config.GetValue("config", "email")
	email = notifier.NewEmailNotifier("smtp.exmail.qq.com:25", "service@laily.me", "Service123")

	// del the comment in file
	rmcomment.InitRm("--", "/*", "*/")

}

func main() {
	var err error
	path, err = config.GetValue("config", "path")
	if err != nil {
		log.Error("get value error: ", err)
	}
	log.Info("read path =: ", path)

	files := getFileList(path)
	records := compareWithRecord(files)
	if sync {
		exe(&records)
	} else {
		log.Info("add '-sync' argument to sync the database")
	}
}

func newRec(name, md5 string) Record {
	r := &Record{}
	r.Name = name
	r.Md5 = md5
	r.Expire = true
	r.Sync = true
	return *r
}

// getFileList to get all the files and select latest file
func getFileList(path string) []os.FileInfo {
	dirs, err := ioutil.ReadDir(path)
	files := make([]os.FileInfo, 0, 20)
	if err != nil {
		log.Error("getFileList read directory error: ", err)
	}
	for _, file := range dirs {
		if file.IsDir() {
			continue
		} else {
			if testTime(strings.Replace(file.Name(), ".sql", "", -1)) {
				files = append(files, file)
			}
		}
	}
	return files
}

// testTime to find file whose name date after today
func testTime(str string) bool {
	nowStr := time.Now().Format("2006-01-02")
	now, _ := time.Parse("2006-01-02", nowStr)
	t, _ := time.Parse("2006-01-02", str)
	return now.Before(t) || now.Equal(t)
}

// compareWithRecord to compare the file if it is modified
func compareWithRecord(files []os.FileInfo) map[string](Record) {
	records := make(map[string]Record)
	keyList := config.GetKeyList("record")
	for _, v := range keyList {
		md5h, _ := config.GetValue("record", v)
		r := newRec(v, md5h)
		records[v] = r
	}

	for _, file := range files {
		rec, ok := records[file.Name()]
		// not exist
		if !ok {
			r := newRec(file.Name(), getFileMD5(path+"/"+file.Name()))
			r.Expire = false
			records[file.Name()] = r
		} else {
			rec.Expire = false
			md5h := getFileMD5(path + "/" + file.Name())
			if md5h == rec.Md5 {
				rec.Sync = false
			} else {
				rec.Md5 = md5h
			}
			records[rec.Name] = rec
		}
	}

	if sync {
		config.DeleteSection("record")
		for _, rec := range records {
			if !rec.Expire {
				config.SetValue("record", rec.Name, rec.Md5)
			}
		}
		goconfig.SaveConfigFile(config, conf)
	}

	if len(records) > 0 {
		log.Info("these files has changed -------------")
		i := 1
		for _, v := range records {
			log.Printf("%d. %s", i, v.Name)
			i++
		}
		log.Info("-------------------------------------")
	} else {
		log.Info("no file has changed")
	}
	return records
}

func getFileMD5(path string) string {
	file, err := os.Open(path)
	if err != nil {
		log.Error("file md5 error: ", err)
	}
	md5h := md5.New()
	io.Copy(md5h, file)
	s := fmt.Sprintf("%x", md5h.Sum([]byte("")))
	return s
}

func (self *Record) display() {
	fmt.Println("name is: ", self.Name)
	fmt.Println("md5 is: ", self.Md5)
	fmt.Println("sync is: ", self.Sync)
	fmt.Println("expire is: ", self.Expire)
}
